<?php
namespace App\Common\Cla\Com\Token;

use App\Common as com;
use App\Common\Cla\Com\Token as nameToken;

class Auth extends TokenBase {

	use TraitKeyDbOperate;

	/**
	 * 权限规则表->类型->权限名
	 * @var int
	 */
	public static int $varAuthRuleTable_Type_AuthName = 1;

	/**
	 * 权限规则表->类型->静态方法
	 * @var int
	 */
	public static int $varAuthRuleTable_Type_ClassStatic = 2;

	/**
	 * 权限规则表->类型->权限名左模糊匹配
	 * @var int
	 */
	public static int $varAuthRuleTable_Type_AuthNameLeftMatch = 3;
	
	/**
	 * 权限规则表
	 * @var array
	 */
	private array $authRuleTable = [];
	/*
	// 示例
	private $authRuleTable = [
		[
			'type' => 1, // [1]权限列表 [2]静态方法
			'data' => [ 'sys.token.info' ],
			'usePolicy' => true,
		],
		[
			'type' => 2, // [1]权限列表 [2]静态方法
			'data' => [
				'class' => '', // 类名
				'funName' => '', // 方法名
				'funParam' => [], // 方法参数
			],
			'usePolicy' => true,
		],
	];
	*/


	/**
	 * 权限名缓存
	 * 数据结构：[
	 * 	'{权限名}' => true, // 是否允许
	 * 	...
	 * ]
	 * @var array
	 */
	private array $authNameCacheIndexOfAuthName = [];

	/**
	 * @param nameToken $tokenObj token对象
	 */
	public function __construct( nameToken $tokenObj ) {
		parent::__construct( $tokenObj );
		//
		$this->loadeAuthRuleTabl();
	}


	/**
	 * 获取权限规则表->类型->权限名
	 * @return int
	 */
	public function getVarAuthRuleTable_Type_AuthName(  ):int {
		return self::$varAuthRuleTable_Type_AuthName;
	}

	/**
	 * 权限规则表->类型->静态方法
	 * @return int
	 */
	public function getVarAuthRuleTable_Type_ClassStatic(  ):int {
		return self::$varAuthRuleTable_Type_ClassStatic;
	}

	/**
	 * 权限规则表->类型->权限名左模糊匹配
	 * @return int
	 */
	public function getVarAuthRuleTable_Type_AuthNameLeftMatch(  ):int {
		return self::$varAuthRuleTable_Type_AuthNameLeftMatch;
	}

	/**
	 * 获取Token->Auth缓存键对象
	 * @return com\Lib\Cache\KeyDB
	 */
	public function keyDbObj(  ):com\Lib\Cache\KeyDB {
		if( $this->_keyDbObj === null ){
			$this->_keyDbObj = com\Lib\Cache\KeyDB::use( 'reb.open.v1.api.token.auth' )->param( $this->tokenObj->data()->getDataId() );
		}
		return $this->_keyDbObj;
	}

	//

	/**
	 * 获取权限列表(允许的权限列表)
	 * @return array 示例：[ 'sys.token.check', 'sys.token.auth.add', ... ]
	 */
	private function getAllowList(  ):array {
		$allowAuthLists = [];
		foreach( $this->authRuleTable as $ruleValue ){
			if( $ruleValue['usePolicy'] ) $allowAuthLists[] = $ruleValue['match'];
		}
		//
		return $allowAuthLists;
	}

	/**
	 * 检查权限是否允许使用
	 * @param string $authName
	 * @return bool [true]允许 [false]不允许
	 */
	public function check( string $authName ):bool {
		// 检查权限缓存
		$authCacheRes = $this->checkCheckCache( $authName );
		if( $authCacheRes !== null ) return $authCacheRes;
		//
		foreach( $this->authRuleTable as $ruleValue ){
			switch( $ruleValue['type'] ){
				case self::$varAuthRuleTable_Type_AuthName:
					// 权限规则表->类型->权限名
					$usePolicy = $this->checkOfAuthName( $authName, $ruleValue );
					break;
				case self::$varAuthRuleTable_Type_ClassStatic:
					// 权限规则表->类型->静态方法
					$usePolicy = $this->checkOfClassStatic( $authName, $ruleValue );
					break;
				case self::$varAuthRuleTable_Type_AuthNameLeftMatch:
					// 权限规则表->类型->权限名左模糊匹
					$usePolicy = $this->checkOfAuthNameLeftMatch( $authName, $ruleValue );
					break;
				default:
					throw com\Lib\Err\Exception::makeOfNew( '', com\Lib\Err\Code::$sysDataError );
			}
			//
			if( null !== $usePolicy ){
				$this->addCheckCache( $authName, $usePolicy );
				return $usePolicy;
			}
			// if( $ruleValue['match'] == $authName ) return $ruleValue['usePolicy'] ? true : false;
		}
		// 匹配失败，拒绝
		return false;
	}

	/**
	 * 检查权限是否允许使用->权限列表
	 * @param string $authName
	 * @param array $ruleValue
	 * @return bool|null [null]未匹配 [true]通过 [false]不通过
	 */
	private function checkOfAuthName( string $authName, array $ruleValue ) {
		if( in_array( $authName, $ruleValue['data'] ) ){
			return $ruleValue['usePolicy'] ? true : false;
		}
		return null;
	}

	/**
	 * 检查权限是否允许使用->权限列表左模糊匹配
	 * @param string $authName
	 * @param array $ruleValue
	 * @return bool|null [null]未匹配 [true]通过 [false]不通过
	 */
	private function checkOfAuthNameLeftMatch( string $authName, array $ruleValue ):bool|null {
		foreach( $ruleValue['data'] as $authValue ){
			if( str_starts_with( $authName, $authValue ) ){
				return (bool)$ruleValue['usePolicy'];
			}else if( $authValue === '*' ){
				return (bool)$ruleValue['usePolicy'];
			}
		}
		//
		return null;
	}

	/**
	 * 检查权限是否允许使用->静态方法
	 * @param string $authName
	 * @param array $ruleValue
	 * @return bool|null [null]未匹配 [true]通过 [false]不通过
	 * @throws
	 */
	private function checkOfClassStatic( string $authName, array $ruleValue ):bool|null {
		// 检查方法是否存在
		if( ! method_exists ( $ruleValue['data']['class'], $ruleValue['data']['funName'] ) ){
			throw com\Lib\Err\Exception::makeOfNew( '', com\Lib\Err\Code::$apiSysNoExist );
		}
		//
		// $usePolicy = $ruleValue['data']['class']::$ruleValue['data']['funName']( $authName, $ruleValue['data']['funParam'] );
		$usePolicy = call_user_func_array( [ $ruleValue['data']['class'], $ruleValue['data']['funName'] ], [ $this, $authName, $ruleValue['data']['funParam'] ] );
		if( ! is_bool( $usePolicy ) && null !== $usePolicy ){
			throw com\Lib\Err\Exception::makeOfNew( '', com\Lib\Err\Code::$sysDataError );
		}
		//
		return $usePolicy;
	}

	/**
	 * 加入到权限缓存
	 * @param string $authName 权限名
	 * @param bool $usePolicy 是否允许。[false]不允许 [true][默认]允许
	 */
/*	public function addAuthNameCache( string $authName, bool $usePolicy = true ) {
		$authCacheIndexOfAuthName = $this->getAuthCacheIndexOfAuthName();
		//
		if( ! isset( $authCacheIndexOfAuthName[ $authName ] ) || $authCacheIndexOfAuthName[ $authName ] !== $usePolicy ){
			$authCacheIndexOfAuthName[ $authName ] = $usePolicy;
			$this->setOfPath( 'authCacheIndexOfAuthName', $authCacheIndexOfAuthName );
		}
	}*/

	/**
	 * 获取权限缓存数据
	 * @param array 权限缓存数据
	 * 数据结构：[
	 * 	'{权限名}' => true, // 是否允许
	 * 	...
	 * ]
	 * @return array
	 */
/*	private function getAuthCacheIndexOfAuthName():array {
		return $this->getCacheOfPath( 'authCacheIndexOfAuthName', [] );
	}*/

	/**
	 * 检查某个权限缓存
	 * @param string $authName 权限名
	 * @return bool|null 是否允许。[false]不允许 [true]允许 [null]未设置
	 */
/*	public function checkAuthNameCache( string $authName ) {
		$authCacheIndexOfAuthName = $this->getAuthCacheIndexOfAuthName();
		return $authCacheIndexOfAuthName[ $authName ] ?? null;
	}*/

	/**
	 * 移除某个权限缓存
	 * @param string $authName 权限名
	 */
/*	public function rmAuthNameCache( string $authName ) {
		$authCacheIndexOfAuthName = $this->getAuthCacheIndexOfAuthName();
		//
		if( isset( $authCacheIndexOfAuthName[ $authName ] ) ){
			unset( $authCacheIndexOfAuthName[ $authName ] );
			$this->setOfPath( 'authCacheIndexOfAuthName', $authCacheIndexOfAuthName );
		}
	}*/

	/**
	 * 获取权限规则表
	 * @return array 权限规则表
	 */
	public function getRuleTable(  ):array {
		return $this->authRuleTable;
	}

	/**
	 * 增加权限规则(权限列表)
	 * @param string $ruleLists 权限列表(单条)，支持数组形式
	 * @param bool $usePolicy 是否允许
	 * @return void
	 * @throws
	 */
	public function addRuleOfAuthName( string $ruleLists, bool $usePolicy = true ):void {
		$this->addRules( [
			[
				self::$varAuthRuleTable_Type_AuthName,
				$ruleLists,
				$usePolicy,
			]
		] );
	}

	/**
	 * 增加权限规则(权限名左模糊匹配)
	 * @param string $ruleLists 权限列表(单条)(注：传入"*"表示全匹配)，支持数组形式
	 * @param bool $usePolicy 是否允许
	 * @return void
	 * @throws
	 */
	public function addRuleOfAuthNameLeftMatch( string $ruleLists, bool $usePolicy = true ):void {
		$this->addRules( [
			[
				self::$varAuthRuleTable_Type_AuthNameLeftMatch,
				$ruleLists,
				$usePolicy,
			]
		] );
	}

	/**
	 * 增加权限规则(支持多个) TODO :: 支持动态权限验证
	 * @param array $ruleLists
	 * 	格式：[
	 * 		[
	 * 			0 => self::$varAuthRuleTable_Type_xxx,
	 * 			1 => 'test_auth',
	 * 		],...
	 * ]
	 * @return void
	 * @throws
	 */
	public function addRules( array $ruleLists ):void {
		// ...
		$authRuleTable = [];
		//
		foreach( $ruleLists as $ruleValue ){
			//
			switch ( $ruleValue[0] ){
				case self::$varAuthRuleTable_Type_AuthName:
					$authRuleTable[] = $this->buildAuthRuleDataOfAuthName( $ruleValue );
					break;
				case self::$varAuthRuleTable_Type_ClassStatic:
					$authRuleTable[] = $this->buildAuthRuleDataOfClass( $ruleValue );
					break;
				case self::$varAuthRuleTable_Type_AuthNameLeftMatch:
					$authRuleTable[] = $this->buildAuthRuleDataOfAuthNameLeftMatch( $ruleValue );
					break;
				default:
					throw com\Lib\Err\Exception::makeOfNew( '', com\Lib\Err\Code::$sysDataError );
			}
		}
		//
		$this->authRuleTable = com\Lib\Fun\Array_::arrayMerges( $this->authRuleTable, $authRuleTable );
		//
		$this->saveAuthRuleTabl();
	}

	/**
	 * 构建权限规则项->权限列表
	 * @param array $ruleValue
	 * @return array
	 * @throws
	 */
	private function buildAuthRuleDataOfAuthName( array $ruleValue ):array {
		/*
		数据：$ruleValue = [ 1, 'sys.token.info' ];
		数据：$ruleValue = [ 1, 'sys.token.info', false ];
		数据：$ruleValue = [ 1, [
								'sys.token.info',
								'sys.token.info2',
								'sys.token.info3',
							], false
						];
		 */
		$ruleRuleData = [
			'type' => self::$varAuthRuleTable_Type_AuthName,
			'data' => [],
			'usePolicy' => (bool)( $ruleValue[2] ?? true ),
		];
		if( is_array( $ruleValue[1] ) ){
			$ruleRuleData['data'] = $ruleValue[1];
		}elseif( is_string( $ruleValue[1] ) ){
			$ruleRuleData['data'][] = $ruleValue[1];
		}else{
			throw com\Lib\Err\Exception::makeOfNew( '', com\Lib\Err\Code::$sysDataError );
		}
		//
		return $ruleRuleData;
	}

	/**
	 * 构建权限规则项->权限列表左模糊匹配
	 * @param array $ruleValue
	 * @return array
	 * @throws
	 */
	private function buildAuthRuleDataOfAuthNameLeftMatch( array $ruleValue ):array {
		/*
		数据：$ruleValue = [ 3, 'sys.token.info' ];
		数据：$ruleValue = [ 3, 'sys.token.info', false ];
		数据：$ruleValue = [ 3, [
								'sys.token.info',
								'sys.token.info2',
								'sys.token.info3',
							], false
						];
		 */
		$ruleRuleData = [
			'type' => self::$varAuthRuleTable_Type_AuthNameLeftMatch,
			'data' => [],
			'usePolicy' => (bool)( $ruleValue[2] ?? true ),
		];
		if( is_array( $ruleValue[1] ) ){
			$ruleRuleData['data'] = $ruleValue[1];
		}elseif( is_string( $ruleValue[1] ) ){
			$ruleRuleData['data'][] = $ruleValue[1];
		}else{
			throw com\Lib\Err\Exception::makeOfNew( '', com\Lib\Err\Code::$sysDataError );
		}
		//
		return $ruleRuleData;
	}

	/**
	 * 构建权限规则项->静态方法
	 * 注：方法说明，funName( $this, $authName, $param )， return bool|null [null]未匹配 [true]通过 [false]不通过
	 * @param array $ruleValue
	 * @return array
	 */
	private function buildAuthRuleDataOfClass( array $ruleValue ):array {
		// 数据：$ruleValue = [ 2, [ com\Cla\App\Rebirth::class => 'authCheck' ], [ 'mode' => 1 ], false ]
		return [
			'type' => self::$varAuthRuleTable_Type_ClassStatic,
			'data' => [
				'class' => $ruleValue[1][0],
				'funName' => $ruleValue[1][1],
				'funParam' => $ruleValue[2] ?? [],
			],
			'usePolicy' => (bool)( $ruleValue[3] ?? true ),
		];
	}

	/**
	 * 移除权限规则
	 * @return void
	 */
	public function rmRule():void {
		// ...
		$this->authRuleTable = [];
		$this->saveAuthRuleTabl();
	}

	/**
	 * 加载权限规则表
	 * @return void
	 */
	private function loadeAuthRuleTabl(  ):void {
		$this->authRuleTable = $this->getOfPath( 'authRuleTable', [] );
	}

	/**
	 * 保存权限规则表到token auth数据区
	 * @return void
	 */
	private function saveAuthRuleTabl(  ):void {
		$this->setOfPath( 'authRuleTable', $this->authRuleTable );
	}

	/**
	 * 获取check权限缓存数据path
	 * @return string
	 */
	private function getCheckCachePath():string {
		return 'authCacheIndexOfAuthName';
	}

	/**
	 * 获取check权限缓存数据
	 * @return array
	 */
	private function getCheckCache():array {
		return $this->getCacheOfPath( $this->getCheckCachePath(), [] );
	}

	/**
	 * 增加check权限缓存
	 * @param string $authName 权限名
		* @param bool $usePolicy 是否允许
		* @return bool [true][必返]
			*/
	public function addCheckCache( string $authName, bool $usePolicy = true ):bool {
			$authCacheIndexOfAuthName = $this->getCheckCache();
			//
			if( ! isset( $authCacheIndexOfAuthName[ $authName ] ) || $authCacheIndexOfAuthName[ $authName ] !== $usePolicy ){
				$authCacheIndexOfAuthName[ $authName ] = $usePolicy;
				$this->setOfPath( $this->getCheckCachePath(), $authCacheIndexOfAuthName );
			}
			//
			return true;
		}

	/**
	 * 检查某个check权限缓存
	 * @param string $authName 权限名
	 * @return bool|null 是否允许。[false]不允许 [true]允许 [null]未设置
	 */
	public function checkCheckCache( string $authName ):bool|null {
			$authCacheIndexOfAuthName = $this->getCheckCache();
		return $authCacheIndexOfAuthName[ $authName ] ?? null;
	}

	/**
	 * 移除某个check权限缓存
	 * @param string|null $authName 权限名 [null]清空整个check权限缓存
	 * @return void
	 */
	public function rmCheckCache( ?string $authName = null ):void {
		if( $authName === null ){
			$this->setOfPath( $this->getCheckCachePath(), [] );
			return;
		}
		$authCacheIndexOfAuthName = $this->getCheckCache();
		//
		if( isset( $authCacheIndexOfAuthName[ $authName ] ) ){
			unset( $authCacheIndexOfAuthName[ $authName ] );
			$this->setOfPath( $this->getCheckCachePath(), $authCacheIndexOfAuthName );
		}
	}

}
